package com.wtpaint.lib.db.db;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;

import com.wtpaint.lib.db.annotation.ZeusExclude;
import com.wtpaint.lib.db.annotation.ZeusField;
import com.wtpaint.lib.db.annotation.ZeusTable;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class ZeusDbDao<T> implements IZeusDbDao<T> {

    private SQLiteDatabase mSqLiteDatabase;

    private Class<T> mEntityClass;

    private String mTableName;

    private boolean isInit;

    private HashMap<String, Field> mCacheFiledMap;//存放成员变量与字段之间的关系

    public boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
        this.mSqLiteDatabase = sqLiteDatabase;
        this.mEntityClass = entityClass;

        if (!isInit) {
            //是否添加了注解 没有添加通过反射的类名来是实现,或者写了个空字符串
            ZeusTable mTableAnn = mEntityClass.getAnnotation(ZeusTable.class);
            if (null == mTableAnn || TextUtils.isEmpty(mTableAnn.value())) {
                mTableName = mEntityClass.getSimpleName();
            } else {
                mTableName = mTableAnn.value();
            }

            if (!mSqLiteDatabase.isOpen()) {
                return false;
            }
            String createSql = getCreateTableSql();
            mSqLiteDatabase.execSQL(createSql);
            mCacheFiledMap = new HashMap<>();
            initCacheMap();
            isInit = true;
        }
        return true;
    }

    private String getCreateTableSql() {
        StringBuilder result = new StringBuilder();
        result.append("create table if not exists ");
        result.append(mTableName).append("(");
        //拿到所有的实体类对象的成员变量来建表
        Field[] fields = mEntityClass.getDeclaredFields();//getFields() -->public
        for (Field field : fields) {
            //获取成员变量类型的字节码
            Class type = field.getType();
            //如果添加了排除注解的
            ZeusExclude excludeAnn = field.getAnnotation(ZeusExclude.class);
            if (null != excludeAnn) {
                continue;
            }
            //如果没有添加注解
            ZeusField fieldAnn = field.getAnnotation(ZeusField.class);
            if (null == fieldAnn || TextUtils.isEmpty(fieldAnn.value())) {
                result.append(field.getName());
            } else {
                result.append(fieldAnn.value());
            }
            if (type == String.class) {
                result.append(" TEXT,");
            } else if (type == Long.class) {
                result.append(" BIGINT,");
            } else if (type == Double.class) {
                result.append(" BIGINT,");
            } else if (type == Integer.class) {
                result.append(" INTEGER,");
            } else if (type == byte[].class) {
                result.append(" BLOB,");
            } else {
                continue;
            }
        }
        if (result.charAt(result.length() - 1) == ',') {
            result.deleteCharAt(result.length() - 1);
        }
        result.append(")");
        return result.toString();
    }


    private void initCacheMap() {
        String sql = "select * from " + mTableName + " limit 1,0";
        Cursor cursor = mSqLiteDatabase.rawQuery(sql, null);
        String[] columnNames = cursor.getColumnNames();//获取到表的所有类名
        Field[] files = mEntityClass.getDeclaredFields();
        for (Field field : files) {
            field.setAccessible(true);
        }
        for (String columnName : columnNames) {
            Field columnField = null;
            for (Field field : files) {
                String fieldName;
                ZeusField fieldAnn = field.getAnnotation(ZeusField.class);
                if (null != fieldAnn && !TextUtils.isEmpty(fieldAnn.value())) {
                    fieldName = fieldAnn.value();
                } else {
                    fieldName = field.getName();
                }
                if (columnName.equals(fieldName)) {
                    columnField = field;
                    break;
                }
            }
            if (columnField != null) {
                mCacheFiledMap.put(columnName, columnField);
            }
        }
        cursor.close();
    }


    @Override
    public long insert(T entity) {

        Map<String, String> values = getValues(entity);

        ContentValues contentValues = getContentValues(values);

        return mSqLiteDatabase.insert(mTableName, null, contentValues);
    }

    @Override
    public long update(T entity, T where) {
        int result = -1;
        Map<String, String> values = getValues(entity);
        ContentValues contentValues = getContentValues(values);
        Map<String, String> whereCause = getValues(where);//key==_id   value=1
        Condition condition = new Condition(whereCause);
        result = mSqLiteDatabase.update(mTableName, contentValues, condition.whereCasue, condition.whereArgs);
        return result;
    }

    @Override
    public int delete(T where) {
        Map<String, String> map = getValues(where);
        Condition condition = new Condition(map);
        return mSqLiteDatabase.delete(mTableName, condition.whereCasue, condition.whereArgs);
    }

    @Override
    public List<T> query(T where) {
        return query(where, null, null, null);
    }

    @Override
    public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
        Map<String, String> map = getValues(where);
        String limitString = null;
        if (startIndex != null && limit != null) {
            limitString = startIndex + " , " + limit;
        }
        Condition condition = new Condition(map);
        Cursor cursor = mSqLiteDatabase.query(mTableName, null, condition.whereCasue, condition.whereArgs, null, null, orderBy, limitString);
        return getResult(cursor, where);
    }

    private Map<String, String> getValues(T entity) {
        HashMap<String, String> result = new HashMap<>();
        Iterator<Field> fieldIterator = mCacheFiledMap.values().iterator();
        while (fieldIterator.hasNext()) {
            Field field = fieldIterator.next();
            field.setAccessible(true);
            try {
                Object obj = field.get(entity);
                if (obj == null) {
                    continue;
                }
                String value = obj.toString();
                String key;

                ZeusField fielAnn = field.getAnnotation(ZeusField.class);
                if (null != fielAnn && !TextUtils.isEmpty(fielAnn.value())) {
                    key = fielAnn.value();
                } else {
                    key = field.getName();
                }
                if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
                    result.put(key, value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return result;
    }


    private ContentValues getContentValues(Map<String, String> values) {
        ContentValues contentValues = new ContentValues();
        Set keys = values.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = values.get(key);
            if (value != null) {
                contentValues.put(key, value);
            }
        }
        return contentValues;
    }

    private List<T> getResult(Cursor cursor, T obj) {
        ArrayList list = new ArrayList();
        Object item = null;
        while (cursor.moveToNext()) {
            try {
                item = obj.getClass().newInstance();//new User();
                Iterator iterator = mCacheFiledMap.entrySet().iterator();//字段-成员变量
                while (iterator.hasNext()) {
                    Map.Entry entry = (Map.Entry) iterator.next();
                    String columnName = (String) entry.getKey();
                    Integer columnIndex = cursor.getColumnIndex(columnName);
                    Field field = (Field) entry.getValue();
                    Class type = field.getType();
                    if (columnIndex != -1) {
                        if (type == String.class) {
                            field.set(item, cursor.getString(columnIndex));
                        } else if (type == Double.class) {
                            field.set(item, cursor.getDouble(columnIndex));
                        } else if (type == Integer.class) {
                            field.set(item, cursor.getInt(columnIndex));
                        } else if (type == Long.class) {
                            field.set(item, cursor.getLong(columnIndex));
                        } else if (type == byte[].class) {
                            field.set(item, cursor.getBlob(columnIndex));
                        } else {
                            continue;
                        }
                    }
                }
                list.add(item);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        cursor.close();
        return list;
    }

    private class Condition {
        private String whereCasue;//"name=? and password=?"
        private String[] whereArgs;//new String[]{"jett"}

        public Condition(Map<String, String> whereCasue) {
            ArrayList list = new ArrayList();//whereArgs里面的内容存入list
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("1=1");
            //取所有的字段名
            Set keys = whereCasue.keySet();
            Iterator iterator = keys.iterator();
            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                String value = whereCasue.get(key);
                if (value != null) {
                    stringBuilder.append(" and " + key + "=?");
                    list.add(value);
                }
            }
            this.whereCasue = stringBuilder.toString();
            this.whereArgs = (String[]) list.toArray(new String[list.size()]);

        }
    }
}
